home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
126-150
/
disk_128
/
mrbackup
/
main.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-06
|
20KB
|
843 lines
/* MRBackup - Amiga Hard Disk Backup Utility
* Filename: Main.c
* Author: Mark R. Rinfret
* Date: 08/01/87
*
* This program has been contributed to the public domain. It represents
* a collection of original work and other public domain offerings that
* were found to be useful in writing this application. To the best of
* my knowledge, this program works as described in the accompanying
* document, but no warranties are made in this regard.
* USE AT YOUR OWN RISK.
*
* If you find this program useful, or if you have comments or suggestions
* for enhancing it, please send email to mark@unisec.USI.COM or U.S. Mail
* to:
* Mark R. Rinfret
* 348 Indian Avenue
* Portsmouth, RI 02871
* 401-846-7639 (home)
* 401-849-4174 (work)
*
* History: (most recent change first)
*
* 09/04/87 -MRR- V1.3: Extracted routines related to the backup function
* and placed them in package Backup.c.
*
* 09/03/87 -MRR- V1.3: Fixed a bug in the routines which handle the
* listing file and pathname.
* Extended the IsCompressed() function to check for
* .ARC and .ZOO files.
*
* 08/22/87 -MRR- V1.2: Extracted global data and definitions and placed
* them in a common include file.
*
* 08/11/87 -MRR- V1.1: BUG! The variable 'back' (backup sequence number)
* was not set to 0 by Backup().
*/
#define MAIN
#include "MRBackup.h"
extern struct Requester *pathrequest;
extern struct Window *pathwindow;
/* Main program - N.S.D.T.! */
main(argc,argv)
int argc;
char *argv[];
{
Initialize();
User(); /* it's in the user's hands */
CleanUp(NULL, 0);
}
/* Initialize the program. */
Initialize()
{
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library", 33L ) ) ) {
CleanUp("Can't open Intuition library!", 20);
}
if (!( mywindow = OpenWindow(&nw) ) )
CleanUp("Can't open program window!", 20);
#ifdef DEBUG
if (!(debugconsole =
Open("CON:0/100/640/40/Debug Info", MODE_NEWFILE)))
CleanUp("Can't open debug console!", 20);
#endif
if (!(console =
Open("CON:0/100/640/90/Progress Report", MODE_NEWFILE)))
CleanUp("Can't open console!", 20);
SetMenuStrip(mywindow, &Titles[0]);
InitPathRequest();
AddGadget(mywindow, &StopGad, -1L);
OnGadget(&StopGad, mywindow, NULL);
now = (struct DateStamp *)
AllocMem((long) sizeof(struct DateStamp), MEMF_PUBLIC);
since = (struct DateStamp *)
AllocMem( (long) sizeof(struct DateStamp), MEMF_PUBLIC);
InitBuffer(); /* Allocate copy/compress buffer */
GetUserPrefs(); /* Get user preferences */
SetSpeech();
}
/* Allocate the buffer used for copy/compress operations. */
InitBuffer()
{
for (bufsize = BUFMAX; bufsize > 2048; bufsize -= 2048 )
if (buffer = AllocMem(bufsize, MEMF_CHIP))
return;
CleanUp("Not enough memory for copy buffer!\n",20);
}
/* Handle program termination.
* Called with:
* msg: termination message or NULL
* code: exit code (0 => normal termination)
*/
CleanUp(msg, code)
char *msg; int code;
{
if (msg)
puts(msg);
#ifdef DEBUG
if (debugconsole) Close(debugconsole);
#endif
if (console) Close(console);
if (listing) fclose(listing);
if (mywindow) {
ClearMenuStrip(mywindow);
CancelPathRequest();
CloseWindow(mywindow);
}
if (IntuitionBase) CloseLibrary(IntuitionBase);
if (now) FreeMem( now, (long) sizeof(struct DateStamp));
if (since) FreeMem( since, (long) sizeof(struct DateStamp));
if (buffer) FreeMem(buffer, bufsize);
exit(code);
}
/* Break a full file specification into its volume and pathname components.
* Called with:
* fullpath: full pathname string (input)
* volume: volume name component, sans colon (output)
* path: pathname component (output)
* Returns:
* status code: 1 => no volume name specified, 0 otherwise
*/
BreakPath(fullpath,volume,path)
char *fullpath, *volume, *path;
{
char c, *fp, *s;
unsigned has_volume = 1; /* assume it has volume component */
fp = fullpath;
s = volume;
if ( index(fp, ':') ) { /* volume name specified? */
s = volume;
while ((c = *fp++) != ':') *s++ = c;
}
else
has_volume = 0;
*s = '\0'; /* terminate volume */
strcpy(path, fp); /* the rest is pathname stuff */
return has_volume;
}
/* Do a quick poll on the STOP gadget.
* Returns 1 if STOP gadget hit, 0 otherwise.
*/
int
CheckStop()
{
ULONG class;
struct Gadget *gadget;
struct IntuiMessage *msg;
int status = 0;
if (msg = (struct IntuiMessage *) GetMsg(mywindow->UserPort)) {
class = msg->Class;
gadget = (struct Gadget *) msg->IAddress;
ReplyMsg(msg);
if (class == GADGETUP && gadget->GadgetID == STOPGAD) {
TypeAndSpeak("I am stopping, as you requested.\n");
status = ERR_ABORT; /* quit */
}
}
return status;
}
#ifdef DEBUG
DebugWrite(msg)
char *msg;
{
Write(debugconsole, msg, (long) strlen(msg));
}
#endif
/* Handle a gadget action.
* Called with:
* window: window that gadget is displayed in
* class: message class (GADGETUP/GADGETDOWN/?)
* addr: pointer to gadget structure
*/
DoGadget(window, class, addr)
struct Window *window; ULONG class; struct Gadget *addr;
{
USHORT id; /* gadget identifier */
struct Gadget *upgad;
ULONG upclass;
struct IntuiMessage *upmsg; /* require gadget up to complete */
id = addr->GadgetID;
#ifdef TESTING
if (class == GADGETDOWN && window == pathwindow) {/* pathname gadget */
ActivateWindow(pathwindow);
do {
Wait(1L << pathwindow->UserPort->mp_SigBit);
if (upmsg = (struct IntuiMessage *)
GetMsg(pathwindow->UserPort)) {
upclass = upmsg->Class;
upgad = (struct Gadget *) upmsg->IAddress;
ReplyMsg(upmsg);
if (upclass == GADGETUP && upgad->GadgetID == id) {
class = GADGETUP; /* use new class */
}
else {
upmsg = NULL;
ActivateGadget(addr, pathwindow, pathrequest);
}
}
}
while (! upmsg );
}
#endif
if (class == GADGETUP) {
#ifdef DEBUG
sprintf(debugmsg,"GADGETUP: %d\n",id);
DebugWrite(debugmsg);
#endif
switch (id) {
case HOMEPATHGAD:
GetHomePath(addr);
break;
case BACKPATHGAD:
GetBackPath(addr);
break;
case LISTPATHGAD:
GetListPath(addr);
break;
case XCLDPATHGAD:
GetXcldPath(addr);
break;
case STOPGAD:
TypeAndSpeak(
"Use the STOP gadget during backup and restore operations.\n");
break;
default:
TypeAndSpeak("Unknown gadget - program error!\n");
break;
}
}
}
/* Get the backup device pathname specification.
* Called with:
* Called with:
* gadget: pointer to relevant string gadget
*
* Side-effects:
* If the pathname specification passes its requirements tests,
* the global string backpath will be set to the pathname.
* Otherwise, homepath is left unchanged and the gadget's string
* is reset to the current contents of homevolume.
*/
GetBackPath(gadget)
struct Gadget *gadget;
{
char volume[VOLUME_MAX+1];
UBYTE *fullpath, path[PATH_MAX+1];
fullpath = (UBYTE *) GadgetString(gadget);
BreakPath(fullpath,volume,path);
if (strlen(volume) != 3 ||
tolower(volume[0]) != 'd' ||
tolower(volume[1]) != 'f' ||
volume[2] < '0' || volume[2] > '3') {
TypeAndSpeak("Backup device must be DF0 through DF3\n");
strcpy(fullpath, backpath); /* restore previous value */
}
else
strcpy(backpath, fullpath); /* use new value */
RefreshGadgets(gadget, pathwindow, NULL);
}
/* An error has occurred. Find out what the user wants to do about it.
* Called with:
* msg: message string
* options: the "set" of options available
* 0 => sorry - no options, ERR_ABORT returned
* Returns:
* error recovery option
*/
int
GetErrOpt(msg, options)
char *msg; int options;
{
int i, mask, option_count = 0, select;
int option_list[NERRCODE];
sprintf(conmsg,"An error has interrupted processing:\n%s\n\n",msg);
TypeAndSpeak(conmsg);
return ERR_ABORT; /* ...implement recovery... */
}
/* Get the home pathname specification (volume and pathname).
* Called with:
* gadget: pointer to relevant string gadget
*
* Side-effects:
* If the pathname specification passes its requirements tests,
* the global string homepath will be set to the pathname.
* Otherwise, homevolume is left unchanged and the gadget's string
* is reset to the current contents of homevolume.
*/
GetHomePath(gadget)
struct Gadget *gadget;
{
UBYTE *fullpath, path[PATH_MAX+1], volume[VOLUME_MAX+1];
RemoveGadget(pathwindow, gadget);
fullpath = (UBYTE *) GadgetString(gadget);
BreakPath(fullpath, volume, path);
if (!IsDir(fullpath)) {
badpath:
TypeAndSpeak(
"Home path must be a disk device with optional directory name!\n");
strcpy(fullpath, homepath); /* restore previous value */
}
else if (strlen(volume) != 3 ||
tolower(volume[0]) != 'd' ||
(tolower(volume[1]) != 'h' && tolower(volume[1]) != 'f') ||
!isdigit(volume[2])) {
#ifdef DEBUG
sprintf(debugmsg,"home device = \"%s\" ? \n",volume);
DebugWrite(debugmsg);
#endif
goto badpath;
}
else {
strcpy(homepath, fullpath);
sprintf(conmsg,"Home path is %s\n", homepath);
WriteConsole(conmsg);
}
ResetStringInfo(gadget->SpecialInfo);
AddGadget(pathwindow, gadget, -1L);
RefreshGadgets(gadget, pathwindow, NULL);
}
/* Get the listing pathname.
* Called with:
* gadget: pointer to relevant string gadget
*
* Side-effects:
*/
GetListPath(gadget)
struct Gadget *gadget;
{
UBYTE *path;
RemoveGadget(pathwindow, gadget);
path = (UBYTE *) GadgetString(gadget);
if (!do_listing) {
TypeAndSpeak("Listing mode is not active. ");
TypeAndSpeak("Your change has been ignored.\n");
badpath:
strcpy(path, listpath);
}
else {
if (strcmp(path, listpath)) { /* not same pathname */
if (OpenList(path)) goto badpath;
strcpy(listpath, path);
sprintf(conmsg,"Listing path is %s\n", listpath);
WriteConsole(conmsg);
}
}
ResetStringInfo(gadget->SpecialInfo);
AddGadget(pathwindow, gadget, -1L);
RefreshGadgets(gadget, pathwindow, NULL);
}
GetXcldPath(gadget)
struct Gadget *gadget;
{
UBYTE *path;
RemoveGadget(pathwindow, gadget);
path = (UBYTE *) GadgetString(gadget);
if (!strcmp(path, excludepath)) /* same pathname */
return;
if (access(path, 0)) { /* can't access the file? */
TypeAndSpeak("I can't access the exclude file!\n");
strcpy(path, excludepath); /* restore the gadget */
}
else {
strcpy(excludepath, path);
exclude_has_changed = true;
sprintf(conmsg,"Exclude path is %s\n", excludepath);
}
ResetStringInfo(gadget->SpecialInfo);
AddGadget(pathwindow, gadget, -1L);
RefreshGadgets(gadget, pathwindow, NULL);
}
/* Output a new header to the listing file. */
Header()
{
if (do_listing) {
fprintf(listing,"\f MRBackup Listing of Volume %s\n\n", destvol);
linecount = 2;
}
}
/* Determine if a file is compressed. This is currently done by looking
* at the file name suffix. A rather dumb assessment of the file type
* is made based on a match to one of these suffixes.
*
* Called with:
* filename: file name string
* Returns:
* 1 => file is compressed, 0 => file is not compressed
*/
int
IsCompressed(filename)
char *filename;
{
#define NSUFFIX 3 /* number of known suffixes */
typedef struct {
char *sufx;
USHORT length;
} T_SUFFIX;
static T_SUFFIX suffixes[NSUFFIX] = {
{".z", 2} ,
{".arc", 4},
{ ".zoo", 4}
};
USHORT i, length;
char *s;
length = strlen(filename); /* get length of argument filename */
for (i = 0; i < NSUFFIX; ++i) {
if (length < suffixes[i].length) continue;
/* We're just interested in the current suffix length. */
s = filename + length - suffixes[i].length;
if (!strcmpc(s, suffixes[i].sufx))
return true; /* suffixes match */
}
return false;
}
/* Output a line to the listing file. Start a new line if necessary.
* The output string is assumed to have no form control characters.
* Called with:
* str: string to be output
*/
ListLine(str)
char *str;
{
if (do_listing) {
if (linecount >= LINES_PER_PAGE) Header();
fprintf(listing," %s\n",str);
fflush(listing);
++linecount;
}
}
/* Create a new directory on the destination disk.
* Called with:
* name: directory pathname
* Returns:
* false => success
* true => failure
* Notes:
* NewDir may be called by NewDisk() or BackupDirs(). You should
* note that a condition of mutual recursion between CheckSize and
* NewDir exists, due to the following:
*
* When NewDir is called from BackupDirs(), it calls CheckSize(),
* which in turn calls NewDisk() if there is no space left.
* The create_dir parameter to CheckSize() (false) prevents a
* secondary call to NewDir() since the job is already being
* performed.
*
* When CheckSize() is called as a result of BackupFiles()
* processing, the create_dir parameter is true so that NewDir()
* will be called if NewDisk() is called, thus creating a
* continuation of the current directory. Confused? Me too :-).
*/
int
NewDir(name)
char *name;
{
char c;
struct Lock *dirlock;
int dirleng;
int errnum;
char dirname[256];
int nameindx = 0, nameleng;
size--; /* takes a block for a directory */
if (CheckSize(false)) /* have room on disk? */
return 1;
strcpy(dirname,destvol); /* start with volume name */
dirleng = strlen(dirname);
nameleng = strlen(name);
/* Parse the pathname, one directory node at a time, creating
* directories as needed.
*/
while (nameindx < nameleng) {
if (nameindx) /* 2nd - nth pass? */
dirname[dirleng++] = '/'; /* directory separator */
while ((c = name[nameindx++]) && c != '/')
dirname[dirleng++] = c;
dirname[dirleng] = '\0'; /* terminate with null */
if (dirlock = Lock(dirname,SHARED_LOCK)) /* subdir exists? */
UnLock(dirlock);
else { /* create subdirectory */
if ((dirlock = CreateDir(dirname))== NULL){
if ((errnum = IoErr())== ERROR_DIRECTORY_NOT_EMPTY){
sprintf(conmsg,
"Directory %s already exists!\n",dirname);
TypeAndSpeak(conmsg);
}
else {
sprintf(conmsg,
"ERROR %d: Unable to create directory %s\n",
errnum,dirname);
TypeAndSpeak(conmsg);
return 1;
}
}
else
UnLock(dirlock);
}
} /* endwhile */
return 0;
}
/* Skip 'n' lines in the listing file.
* Called with:
* n: number of lines to skip
*/
NewLine(n)
int n;
{
if (do_listing) {
if (n + linecount >= LINES_PER_PAGE)
Header();
else while (n--) {
fputc('\n', listing);
++linecount;
}
}
}
/* Open the listing file.
* Called with:
* name: file or device pathname
* Returns:
* status (0 => success)
*/
int
OpenList(name)
char *name;
{
int status = 0;
if (listing) fclose(listing); /* prior listing file open? */
if (!(listing = fopen(name, "w"))) {
status = errno;
sprintf(conmsg,
"I can't open the listing file \"%s\", error %d.\n",
name, status);
TypeAndSpeak(conmsg);
}
else
linecount = LINES_PER_PAGE;
return status;
}
/* Reset the variables in a StringInfo structure.
* Called with:
* s: pointer to a StringInfo
*/
ResetStringInfo(s)
struct StringInfo *s;
{
*(s->UndoBuffer) = '\0';
s->BufferPos = 0;
s->DispPos = 0;
s->UndoPos = 0;
s->NumChars = strlen(s->Buffer);
}
/* Enable/disable speech capability, based on global 'do_speech'. */
SetSpeech()
{
if (do_speech) {
if (!SpeechOn()) {
DateStamp(now);
if (now->ds_Tick < 1500)
Say("That feels good! Thanks for turning me on!");
else
Say("Hi. How may I help you?");
}
}
else
SpeechOff();
}
Speak(msg)
char *msg;
{
if (do_speech) Say(msg);
}
/* Perform a case-insensitive string compare.
* Called with:
* s1, s2: strings to be compared
* Returns:
* comparison code: 0 => strings match
* <0 => s1 < s2
* >0 => s1 > s2
*/
int
strcmpc(s1, s2)
register char *s1, *s2;
{
int c1, c2, cd;
do {
c1 = tolower(*s1++);
c2 = tolower(*s2++);
if (cd = (c1 - c2)) break;
} while (c1 && c2);
return cd;
}
/* Type a message to the console and optionally "speak" it.
* Called with:
* msg: message string
*/
TypeAndSpeak(msg)
char *msg;
{
WriteConsole(msg);
if (do_speech) Say(msg);
}
/* Handle IDCMP messages generated by user actions. */
User()
{
ULONG class; /* message class */
USHORT code; /* message code */
USHORT gadgid; /* gadget ID */
APTR Iadr; /* address field from message */
struct IntuiMessage *msg; /* Intuition message pointer */
struct Window *msgwindow; /* window message occurred in */
USHORT quit = 0;
SHORT x,y; /* mouse x and y position */
ULONG waitbits;
waitbits = (1L << mywindow->UserPort->mp_SigBit) |
(1L << pathwindow->UserPort->mp_SigBit);
#ifdef DEBUG
sprintf(debugmsg,"User: waitbits = %08lx\n", waitbits);
DebugWrite(debugmsg);
#endif
while (!quit) {
ActivateWindow(mywindow);
Wait(waitbits);
while (!quit) {
if (!(msg = (struct IntuiMessage *)
GetMsg(mywindow->UserPort)))
if (!(msg = (struct IntuiMessage *)
GetMsg(pathwindow->UserPort)))
break;
class = msg->Class;
code = msg->Code;
Iadr = msg->IAddress;
x = msg->MouseX;
y = msg->MouseY;
msgwindow = msg->IDCMPWindow;
ReplyMsg(msg); /* acknowledge the message */
#ifdef DEBUG
sprintf(debugmsg,"Message class: 0x%lx, code: 0x%x\n",
class, code);
#endif
switch (class) {
case CLOSEWINDOW:
++quit;
break;
case GADGETUP:
case GADGETDOWN:
DoGadget(msgwindow, class, Iadr);
break;
case MENUPICK:
quit = UserMenu(code);
break;
default:
break; /* ignore the rest */
} /* end switch(class) */
}
}
}
/* Handle a menu selection.
* Called with:
* xcode: menu selection code
* Returns:
* status code (1 => Quit was selected)
*/
int
UserMenu(xcode)
USHORT xcode;
{
USHORT code = xcode;
struct MenuItem *item;
USHORT itemnum,menunum;
while (code != MENUNULL) {
menunum = MENUNUM(code);
itemnum = ITEMNUM(code);
#ifdef DEBUG
sprintf(debugmsg,"menu = %d, item = %d\n",menunum,itemnum);
DebugWrite(debugmsg);
#endif
item = ItemAddress(&Titles[0], code);
switch (menunum) {
case MENU_PROJECT: /* Project Menu */
switch (itemnum) {
case ITEM_BACKUP: /* Backup */
Backup();
break;
case ITEM_RESTORE: /* Restore */
Restore();
break;
case ITEM_ABOUT: /* About */
About();
break;
case ITEM_QUIT: /* Quit */
return 1;
default:
DisplayBeep(NULL);
break;
}
break;
case MENU_FLAGS: /* Flags Menu */
switch (itemnum) {
case ITEM_COMPRESS: /* Compression */
do_compress = IsChecked(item);
break;
case ITEM_NOCOMPRESS: /* No Compression */
do_compress = !IsChecked(item);
break;
case ITEM_LIST: /* Listing */
do_listing = IsChecked(item);
break;
case ITEM_NOLIST: /* No Listing */
do_listing = !IsChecked(item);
break;
case ITEM_SPEECH: /* Speech */
do_speech = IsChecked(item);
SetSpeech();
break;
case ITEM_NOSPEECH: /* No Speech */
do_speech = !IsChecked(item);
SetSpeech();
break;
default:
DisplayBeep(NULL);
break;
}
}
#define EXTENDED_SELECT_WORKS
#ifdef EXTENDED_SELECT_WORKS
code = item->NextSelect;
/* This next line is a kludge. Testing has revealed that
* the NextSelect field is returning 0000x. Why?
*/
if (!code) break;
#else
break;
#endif
}
return 0;
}
/* Write a message to the console window.
* Called with:
* msg: message string
*/
WriteConsole(msg)
char *msg;
{
Write(console, msg, (long) strlen(msg));
}